"""
日志中间件 - 自动记录请求/响应信息和性能指标
支持结构化日志、请求追踪、性能监控等
"""

import time
import uuid
import json
from typing import Dict, Any, Optional, List, Callable
from fastapi import Request, Response
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.types import ASGIApp
import asyncio
from dataclasses import dataclass, asdict

from ..logging.manager import get_logger, get_performance_logger, StructuredLogger, PerformanceLogger

@dataclass
class RequestInfo:
    """请求信息"""
    request_id: str
    method: str
    url: str
    path: str
    query_params: Dict[str, str]
    headers: Dict[str, str]
    client_ip: str
    user_agent: Optional[str] = None
    content_type: Optional[str] = None
    content_length: Optional[int] = None

@dataclass
class ResponseInfo:
    """响应信息"""
    status_code: int
    headers: Dict[str, str]
    content_length: Optional[int] = None
    content_type: Optional[str] = None

@dataclass
class RequestMetrics:
    """请求指标"""
    start_time: float
    end_time: float
    duration_ms: float
    memory_usage: Optional[int] = None
    cpu_usage: Optional[float] = None

class LoggingMiddleware(BaseHTTPMiddleware):
    """日志记录中间件"""
    
    def __init__(
        self,
        app: ASGIApp,
        logger_name: str = "request",
        log_request_body: bool = False,
        log_response_body: bool = False,
        exclude_paths: Optional[List[str]] = None,
        include_headers: bool = True,
        sensitive_headers: Optional[List[str]] = None,
        max_body_size: int = 1024 * 10  # 10KB
    ):
        super().__init__(app)
        self.logger = get_logger(logger_name)
        self.perf_logger = get_performance_logger(logger_name)
        self.log_request_body = log_request_body
        self.log_response_body = log_response_body
        self.exclude_paths = exclude_paths or []
        self.include_headers = include_headers
        self.sensitive_headers = sensitive_headers or [
            "authorization", "cookie", "x-api-key", "x-auth-token"
        ]
        self.max_body_size = max_body_size
    
    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        """处理请求"""
        # 生成请求ID
        request_id = str(uuid.uuid4())
        
        # 检查是否排除此路径
        if self._should_exclude_path(request.url.path):
            return await call_next(request)
        
        # 记录请求开始
        start_time = time.time()
        request_info = await self._extract_request_info(request, request_id)
        
        # 设置请求上下文
        logger = self.logger.with_context(
            request_id=request_id,
            component="http_request"
        )
        
        # 记录请求
        await self._log_request(logger, request, request_info)
        
        try:
            # 处理请求
            response = await call_next(request)
            
            # 记录响应
            end_time = time.time()
            duration_ms = (end_time - start_time) * 1000
            
            response_info = self._extract_response_info(response)
            metrics = RequestMetrics(
                start_time=start_time,
                end_time=end_time,
                duration_ms=duration_ms
            )
            
            await self._log_response(logger, response, response_info, metrics)
            
            # 添加请求ID到响应头
            response.headers["X-Request-ID"] = request_id
            
            return response
            
        except Exception as e:
            # 记录异常
            end_time = time.time()
            duration_ms = (end_time - start_time) * 1000
            
            logger.error(
                f"Request failed: {str(e)}",
                exc_info=e,
                request=asdict(request_info),
                duration_ms=duration_ms,
                status="error"
            )
            
            # 返回错误响应
            return JSONResponse(
                status_code=500,
                content={"error": "Internal Server Error", "request_id": request_id},
                headers={"X-Request-ID": request_id}
            )
    
    def _should_exclude_path(self, path: str) -> bool:
        """检查是否应该排除此路径"""
        return any(excluded in path for excluded in self.exclude_paths)
    
    async def _extract_request_info(self, request: Request, request_id: str) -> RequestInfo:
        """提取请求信息"""
        # 获取客户端IP
        client_ip = request.client.host if request.client else "unknown"
        forwarded_for = request.headers.get("x-forwarded-for")
        if forwarded_for:
            client_ip = forwarded_for.split(",")[0].strip()
        
        # 过滤敏感头信息
        headers = dict(request.headers)
        if not self.include_headers:
            headers = {}
        else:
            headers = self._filter_sensitive_headers(headers)
        
        return RequestInfo(
            request_id=request_id,
            method=request.method,
            url=str(request.url),
            path=request.url.path,
            query_params=dict(request.query_params),
            headers=headers,
            client_ip=client_ip,
            user_agent=request.headers.get("user-agent"),
            content_type=request.headers.get("content-type"),
            content_length=request.headers.get("content-length")
        )
    
    def _extract_response_info(self, response: Response) -> ResponseInfo:
        """提取响应信息"""
        headers = dict(response.headers)
        if not self.include_headers:
            headers = {}
        
        return ResponseInfo(
            status_code=response.status_code,
            headers=headers,
            content_type=response.headers.get("content-type"),
            content_length=response.headers.get("content-length")
        )
    
    def _filter_sensitive_headers(self, headers: Dict[str, str]) -> Dict[str, str]:
        """过滤敏感头信息"""
        filtered = {}
        for key, value in headers.items():
            if key.lower() in self.sensitive_headers:
                filtered[key] = "***"
            else:
                filtered[key] = value
        return filtered
    
    async def _log_request(self, logger: StructuredLogger, request: Request, request_info: RequestInfo):
        """记录请求"""
        log_data = {
            "event": "request_start",
            "request": asdict(request_info)
        }
        
        # 记录请求体
        if self.log_request_body and request.method in ["POST", "PUT", "PATCH"]:
            try:
                body = await self._read_request_body(request)
                if body and len(body) <= self.max_body_size:
                    log_data["request"]["body"] = body
                elif body:
                    log_data["request"]["body_size"] = len(body)
                    log_data["request"]["body_truncated"] = True
            except Exception as e:
                logger.warning(f"Failed to read request body: {str(e)}")
        
        logger.info("HTTP Request received", **log_data)
    
    async def _log_response(
        self, 
        logger: StructuredLogger, 
        response: Response, 
        response_info: ResponseInfo, 
        metrics: RequestMetrics
    ):
        """记录响应"""
        log_data = {
            "event": "request_complete",
            "response": asdict(response_info),
            "metrics": asdict(metrics),
            "status": "success" if response.status_code < 400 else "error"
        }
        
        # 记录响应体
        if self.log_response_body:
            try:
                body = await self._read_response_body(response)
                if body and len(body) <= self.max_body_size:
                    log_data["response"]["body"] = body
                elif body:
                    log_data["response"]["body_size"] = len(body)
                    log_data["response"]["body_truncated"] = True
            except Exception as e:
                logger.warning(f"Failed to read response body: {str(e)}")
        
        # 性能日志
        if metrics.duration_ms > 1000:  # 超过1秒的请求
            logger.warning("Slow request detected", **log_data)
        else:
            logger.info("HTTP Request completed", **log_data)
    
    async def _read_request_body(self, request: Request) -> Optional[str]:
        """读取请求体"""
        try:
            body = await request.body()
            if body:
                return body.decode('utf-8')
        except Exception:
            pass
        return None
    
    async def _read_response_body(self, response: Response) -> Optional[str]:
        """读取响应体"""
        try:
            if hasattr(response, 'body'):
                if isinstance(response.body, bytes):
                    return response.body.decode('utf-8')
                elif isinstance(response.body, str):
                    return response.body
        except Exception:
            pass
        return None

class PerformanceMiddleware(BaseHTTPMiddleware):
    """性能监控中间件"""
    
    def __init__(
        self,
        app: ASGIApp,
        logger_name: str = "performance",
        slow_request_threshold: float = 1.0,  # 慢请求阈值（秒）
        enable_memory_tracking: bool = False
    ):
        super().__init__(app)
        self.logger = get_logger(logger_name)
        self.perf_logger = get_performance_logger(logger_name)
        self.slow_request_threshold = slow_request_threshold
        self.enable_memory_tracking = enable_memory_tracking
    
    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        """处理请求性能监控"""
        start_time = time.time()
        start_memory = self._get_memory_usage() if self.enable_memory_tracking else None
        
        try:
            response = await call_next(request)
            
            # 计算性能指标
            end_time = time.time()
            duration = end_time - start_time
            end_memory = self._get_memory_usage() if self.enable_memory_tracking else None
            
            # 记录性能数据
            perf_data = {
                "method": request.method,
                "path": request.url.path,
                "status_code": response.status_code,
                "duration_seconds": duration,
                "duration_ms": duration * 1000,
            }
            
            if self.enable_memory_tracking and start_memory and end_memory:
                perf_data["memory_used_mb"] = (end_memory - start_memory) / 1024 / 1024
            
            # 检查是否为慢请求
            if duration > self.slow_request_threshold:
                self.logger.warning("Slow request detected", **perf_data)
            else:
                self.logger.info("Request performance", **perf_data)
            
            # 添加性能头
            response.headers["X-Response-Time"] = f"{duration * 1000:.2f}ms"
            
            return response
            
        except Exception as e:
            end_time = time.time()
            duration = end_time - start_time
            
            self.logger.error(
                "Request failed with performance data",
                method=request.method,
                path=request.url.path,
                duration_seconds=duration,
                duration_ms=duration * 1000,
                exc_info=e
            )
            raise
    
    def _get_memory_usage(self) -> Optional[int]:
        """获取内存使用量（字节）"""
        try:
            import psutil
            import os
            process = psutil.Process(os.getpid())
            return process.memory_info().rss
        except ImportError:
            return None

class AccessLogMiddleware(BaseHTTPMiddleware):
    """访问日志中间件（类似Nginx访问日志）"""
    
    def __init__(
        self,
        app: ASGIApp,
        logger_name: str = "access",
        log_format: str = "combined"  # combined, common, custom
    ):
        super().__init__(app)
        self.logger = get_logger(logger_name)
        self.log_format = log_format
    
    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        """记录访问日志"""
        start_time = time.time()
        
        response = await call_next(request)
        
        duration = time.time() - start_time
        
        # 格式化访问日志
        if self.log_format == "combined":
            log_line = self._format_combined_log(request, response, duration)
        elif self.log_format == "common":
            log_line = self._format_common_log(request, response, duration)
        else:
            log_line = self._format_custom_log(request, response, duration)
        
        self.logger.info(log_line)
        
        return response
    
    def _format_combined_log(self, request: Request, response: Response, duration: float) -> str:
        """组合日志格式"""
        client_ip = request.client.host if request.client else "-"
        method = request.method
        path = request.url.path
        query = f"?{request.url.query}" if request.url.query else ""
        status = response.status_code
        size = response.headers.get("content-length", "-")
        referer = request.headers.get("referer", "-")
        user_agent = request.headers.get("user-agent", "-")
        
        return (
            f'{client_ip} - - [{time.strftime("%d/%b/%Y:%H:%M:%S +0000")}] '
            f'"{method} {path}{query} HTTP/1.1" {status} {size} '
            f'"{referer}" "{user_agent}" {duration:.3f}s'
        )
    
    def _format_common_log(self, request: Request, response: Response, duration: float) -> str:
        """通用日志格式"""
        client_ip = request.client.host if request.client else "-"
        method = request.method
        path = request.url.path
        query = f"?{request.url.query}" if request.url.query else ""
        status = response.status_code
        size = response.headers.get("content-length", "-")
        
        return (
            f'{client_ip} - - [{time.strftime("%d/%b/%Y:%H:%M:%S +0000")}] '
            f'"{method} {path}{query} HTTP/1.1" {status} {size} {duration:.3f}s'
        )
    
    def _format_custom_log(self, request: Request, response: Response, duration: float) -> str:
        """自定义日志格式（JSON）"""
        return json.dumps({
            "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
            "client_ip": request.client.host if request.client else None,
            "method": request.method,
            "path": request.url.path,
            "query": request.url.query,
            "status_code": response.status_code,
            "content_length": response.headers.get("content-length"),
            "referer": request.headers.get("referer"),
            "user_agent": request.headers.get("user-agent"),
            "duration_seconds": duration
        })

# 便捷函数
def create_logging_middleware(
    log_requests: bool = True,
    log_performance: bool = True,
    log_access: bool = False,
    **kwargs
) -> List[BaseHTTPMiddleware]:
    """创建日志中间件组合"""
    middlewares = []
    
    if log_requests:
        middlewares.append(LoggingMiddleware(**kwargs))
    
    if log_performance:
        middlewares.append(PerformanceMiddleware(**kwargs))
    
    if log_access:
        middlewares.append(AccessLogMiddleware(**kwargs))
    
    return middlewares
